from pydantic import BaseModel, Field
from pydantic_settings import (
    SettingsConfigDict,
    BaseSettings
)
from redis.asyncio import Redis
from miniopy_async import Minio
from functools import lru_cache
from pathlib import Path

PROJECT_DIR = Path(__file__).parent.parent

BASE_DIR = PROJECT_DIR.parent

class ServerSettings(BaseSettings):
    """Server configuration
    """
    HOST: str = "0.0.0.0"
    
    PORT: int = 8000
        
    RELOAD: bool = False
    
    WORKERS: int = 1
    
    THREADS: int = 1
    
    PREFIX: str = ''
    
    LOG_LEVEL: str = 'debug'
    
    model_config = SettingsConfigDict(env_prefix="SERVER_", env_file=".env")
    
    
class RedisSettings(BaseSettings):
    """Redis configuration
    """
    
    URL: str = "redis://localhost:6379/0"
    """A Redis connection URL"""
    CONNECT_TIMEOUT: int = 5
    """Length of time to wait (in seconds) for a connection to become
    active"""
    HEALTH_CHECK_INTERVAL: int = 5
    """Length of time to wait (in seconds) before testing connection health"""
    SOCKET_KEEPALIVE: bool = True
    """Length of time to keepalive"""
    
    ENABLED: bool = False
    """if enable redis"""
    
    @property
    def client(self) -> Redis:
        return self.get_client()
    
    def get_client(self) -> Redis:
        return Redis.from_url(
            url=self.URL,
            encoding="utf-8",
            decode_responses=False,
            socket_connect_timeout=self.CONNECT_TIMEOUT,
            socket_keepalive=self.SOCKET_KEEPALIVE,
            health_check_interval=self.HEALTH_CHECK_INTERVAL,
        )
        
    model_config = SettingsConfigDict(env_prefix="REDIS_", env_file=".env")
    
    
class AppSettings(BaseSettings):
    """Application configuration
    """
    ENV: str
    """App run enviroment profile"""
    DEBUG: bool = False
    """Run 'Application' with 'debug=${DEBUG}' """
    TITLE: str = "Title"
    """Application title"""
    NAME: str = "fastapi-web"
    """Application name"""
    VERSION: str = "1.0"
    """Application version"""
    DESCRIPTION: str = "Description"
    """Application description"""
    ALLOWED_CORS_ORIGINS: list[str] = ["*"]
    """Allowed CORS Origins"""
    ALLOWED_CORS_METHODS: list = ["*"]
    """Allowed CORS Methods"""
    ALLOWED_CORS_HEADERS: list = ["*"]
    """Allowed CORS Headers"""
    ALLOWED_CORS_CREDENTIALS: bool = True
    """Allowed CORS Credentials"""
    ENABLE_LIMITER: bool = False
    """if enable request limiter"""
    REQUEST_LIMITER_REDIS_PREFIX: str = 'fm:limiter'
    """prefix in request limiter"""
    MIDDLEWARE_ACCESS: bool = True
    """if enable access log"""
    MIDDLEWARE_CORS: bool = True
    """if enable cors"""
    
    DATETIME_FORMAT: str = "%Y-%m-%d %H:%M:%S"
    """Time format"""
    
    model_config = SettingsConfigDict(env_prefix="APP_", env_file=".env")
    
class LogSettings(BaseSettings):
    """Log configuration
    """
    PATH: str = str(BASE_DIR / "logs")
    """log path, default to base_dir / logs """
    INFO_DIR: str = "info"
    """info log directory, default to logs / info"""
    INFO_FILE: str = "info_{time:YYYY-MM-DD}.log"
    """info log name"""
    ERROR_DIR: str = "error"
    """error log directory, default to logs / error"""
    ERROR_FILE: str = "error_{time:YYYY-MM-DD}.log"
    """error log name"""
    FORMAT: bool = True
    """if log to file"""
    
    model_config = SettingsConfigDict(env_prefix="LOG_", env_file=".env")
    
class ThreadPoolSettings(BaseSettings):
    """Thread pool configuration
    """
    
    WORKERS: int = 15
    """workers in thread"""
    TIMEOUT: int = 0
    """task timeout in thread"""
    ENABLED: bool = False
    """if enable thread pool"""
    
    model_config = SettingsConfigDict(env_prefix="THREAD_", env_file=".env")
    
    
class TaskSettings(BaseSettings):
    """Task configuration
    """
    CONCURRENCY: int = 10
    """concurrency tasks limit"""
    PENDING: int = 10
    """pending tasks limit"""
    ENABLED: bool = False
    """if enable task scheduler"""
    
    model_config = SettingsConfigDict(env_prefix="TASK_", env_file=".env")
    
    
class MinIOSettings(BaseSettings):
    """MinIO configuration
    """
    URL: str = "localhost:9000"
    """Endpoint [ip:port] in minio"""
    ACCESSKEY: str = "admin"
    """access key in minio"""
    SECRETKEY: str = "admin"
    """secrect key in minio"""
    BUCKETNAME: str = "bucket"
    """bucket name in minio"""
    SECURE: bool = False
    """secure in minio, use http if secure == False else https"""
    
    @property
    def client(self) -> Minio:
        return self.get_client()
    
    def get_client(self) -> Minio:
        return Minio(
            endpoint=self.URL,
            access_key=self.ACCESSKEY,
            secret_key=self.SECRETKEY,
            secure=self.SECURE
        )
    
    model_config = SettingsConfigDict(env_prefix="MINIO_", env_file=".env")
    
    
class Settings(BaseModel):
    app: AppSettings = Field(default_factory=AppSettings)
    server: ServerSettings = Field(default_factory=ServerSettings)
    thread: ThreadPoolSettings = Field(default_factory=ThreadPoolSettings)
    log: LogSettings = Field(default_factory=LogSettings)
    task: TaskSettings = Field(default_factory=TaskSettings)
    redis: RedisSettings = Field(default_factory=RedisSettings)
    minio: MinIOSettings = Field(default_factory=MinIOSettings)
    
    project_dir: str = str(PROJECT_DIR)
    """src code dir"""
    
    base_dir: str = str(BASE_DIR)
    """base dir"""
    
@lru_cache(maxsize=1)
def get_settings() -> Settings:
    return Settings()